#include "fescreenbuilder_private.h"
#include "fescreenwidget_private.h"
#include "fescreenupdater_private.h"
#include "fescalelayout_private.h"
#include "febugwidget_private.h"
#include "fewidgetfactory.h"
#include <QWidget>
#include <QLayout>
#include <QVBoxLayout>
#include <QHBoxLayout>

FeScreenBuilder::FeScreenBuilder()
{
	wf = 0;
	ads = 0;
}

FeScreenBuilder::~FeScreenBuilder()
{
}

FeDOMDocument FeScreenBuilder::document() const
{
	return doc;
}

void FeScreenBuilder::setDocument(const FeDOMDocument &v)
{
	doc = v;
}

FeWidgetFactory *FeScreenBuilder::widgetFactory() const
{
	return wf;
}

void FeScreenBuilder::setWidgetFactory(FeWidgetFactory *v)
{
	wf = v;
}

FeActualDataService *FeScreenBuilder::actualDataService() const
{
	return ads;
}

void FeScreenBuilder::setActualDataService(FeActualDataService *v)
{
	ads = v;
}

QWidget *FeScreenBuilder::buildScreen(const QString &name, QWidget *parent)
{
	widgets = QMap<QString, QWidget *>();

	QWidget *w = 0;
	if(!doc.isNull())
	{
		w = buildScreenWidget(doc.screen(), parent);
		w->setObjectName(name);
	}
	return w;
}

QWidget *FeScreenBuilder::buildScreenWidget(const FeDOMScreen &screen, QWidget *parent)
{
	Q_ASSERT(!screen.isNull());

	QWidget *w = new FeScreenWidget(parent);
	if(parent)
	{
		w->setGeometry(0, 0, parent->width(), parent->height());
	}
	setupProperties(w, screen.properties());
	QLayout *l = buildLayout(screen.layout(), w);
	w->setLayout(l);

	Q_ASSERT(w->layout() == l);

	setupSignalBinding(screen.signalBinding(), w);
	
	return w;
}

QLayout *FeScreenBuilder::buildLayout(const FeDOMLayout &layout, QWidget *widget)
{
	Q_ASSERT(!layout.isNull());
	QLayout *l = 0;
	switch(layout.nodeType())
	{
	case FeDOMNode::VBoxLayout:
		{
			const FeDOMVBoxLayout &l_node = (const FeDOMVBoxLayout &)layout;
			QVBoxLayout *ll = new QVBoxLayout;
			ll->setMargin(l_node.margin());
			ll->setSpacing(l_node.spacing());
			l = ll;
		}
		break;

	case FeDOMNode::HBoxLayout:
		{
			const FeDOMHBoxLayout &l_node = (const FeDOMHBoxLayout &)layout;
			QHBoxLayout *ll = new QHBoxLayout;
			ll->setMargin(l_node.margin());
			ll->setSpacing(l_node.spacing());
			l = ll;
		}
		break;

	case FeDOMNode::ScaleLayout:
		{
			const FeDOMScaleLayout &l_node = (const FeDOMScaleLayout &)layout;
			FeScaleLayout *ll = new FeScaleLayout(l_node.size());
			l = ll;
		}
		break;

	default:
		Q_ASSERT_X(false, __FILE__ , "Unsupported layout type");
		break;
	}

	const QList<FeDOMLayoutItem> items = layout.items();
	for(QList<FeDOMLayoutItem>::const_iterator i = items.begin(); i != items.end(); ++i)
	{
		switch(i->nodeType())
		{
		case FeDOMNode::VBoxLayout:
		case FeDOMNode::HBoxLayout:
		case FeDOMNode::ScaleLayout:
			{
				const FeDOMLayout &sub_node = (const FeDOMLayout &)(*i);
				QLayout *sub_l = buildLayout(sub_node, widget);
				l->addItem(sub_l);
			}
			break;

		case FeDOMNode::Widget:
			{
				const FeDOMWidget &sub_node = (const FeDOMWidget &)(*i);
				QWidget *sub_w = buildWidget(sub_node, widget);
				l->addItem(new QWidgetItem(sub_w));
			}
			break;

		default:
			Q_ASSERT_X(false, __FILE__ , "Unsupported layout item type");
			break;
		}
	}

	return l;
}

QWidget *FeScreenBuilder::buildWidget(const FeDOMWidget &widget, QWidget *parent)
{
	QWidget *w = 0;
	if(!widgets.contains(widget.name()))
	{
		QString type = widget.type();
		if(wf)
		{
			w = wf->createWidget(type);
		}
		
		if(!w)
		{
			w = new FeBugWidget;
		}
		
		w->setParent(parent);
		w->setObjectName(widget.name());
		widgets.insert(widget.name(), w);

#ifdef FESCREENBUILDER_DEBUG
		{
			QByteArray obj_name = w->objectName().toLocal8Bit();
			qDebug("Add widget %s[%s]",
					w->metaObject()->className(),
					obj_name.constData());
		}
#endif
		setupProperties(w, widget.properties());

#ifdef FESCREENBUILDER_DEBUG
		{
			QRect r = w->geometry();
			qDebug("Widget geometry: %d,%d,%d,%d",
				r.x(), r.y(), r.width(), r.height());
		}
#endif
	}
	else
	{
#ifdef FESCREENBUILDER_DEBUG
		{
			QByteArray obj_name = widget.name().toLocal8Bit();
			qDebug("Dublicate widget name: %s",
					obj_name.constData());
		}
#endif
	}
		
	return w;
}

void FeScreenBuilder::setupSignalBinding(const FeDOMSignalBinding &binding, QWidget *screen)
{
	if(ads)
	{
		FeScreenUpdater *updater = new FeScreenUpdater(ads, screen);
		
		QList<FeDOMSignalBind> lst = binding.bindings();
		for(QList<FeDOMSignalBind>::const_iterator i = lst.begin();
			i != lst.end(); ++i)
		{
			const FeDOMSignalBind &bind = *i;
			QMap<QString, QWidget *>::const_iterator wi = widgets.find(bind.widgetName());
			if(wi != widgets.end())
			{
				QWidget *w = wi.value();
				updater->addBind(bind.signalId(), w, bind.propertyName());
			}

		}
		updater->start();
	}
}

void FeScreenBuilder::setupProperties(QWidget *widget, const QList<FeDOMProperty> &properties)
{
	Q_ASSERT(widget);
	for(QList<FeDOMProperty>::const_iterator i = properties.begin();
		i != properties.end(); ++i)
	{
		Q_ASSERT(!i->isNull());

		QByteArray name = i->name().toLatin1();
		QVariant val = i->value();

#ifdef FESCREENBUILDER_DEBUG
		{
			QByteArray obj_name = widget->objectName().toLocal8Bit();
			QByteArray val_str = val.toString().toLocal8Bit();
			qDebug("Set property %s[%s].%s = %s",
				widget->metaObject()->className(),
				obj_name.constData(),
				name.constData(),
				val_str.constData());
		}
#endif
		widget->setProperty(name.constData(), val);
	}
}
